package gcontent.launch;

import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Properties;
import java.util.Vector;
import java.util.HashMap;

import gcontent.channel.Channel;
import gcontent.channel.ChannelManager;
import gcontent.channel.error.ChannelEx;
import gcontent.config.Configure;
import gcontent.entities.Content;
import gcontent.entities.Site;
import gcontent.entities.content.error.ContentEx;
import gcontent.entities.site.SiteURL;
import gcontent.entities.site.error.SiteEx;
import gcontent.filter.Filter;
import gcontent.filter.FilterContentChain;
import gcontent.filter.FilterSiteChain;
import gcontent.record.Record;
import gcontent.record.db.RecordDB;
import gcontent.record.error.RecordEx;
import gcontent.rsrc.ThreadResources;
import gcontent.utils.URLUtils;

public class SpiderThread extends Thread {
	
	public enum MODE {
		ANY_DOMAIN,
		FIRST_ONE_DOMAIN,
	};
	
	private Site root_site;
	private RecordDB record_data;
	private HashMap<String, Vector<Filter>> content_filter;	
	private HashMap<String, Vector<Filter>> site_filter;
	private int id;

	private MODE mode = MODE.ANY_DOMAIN;
	private int domain_toks = -1;
	
	public SpiderThread(SiteURL root_site) throws Exception
	{	
		Record record;
		
		record = Configure.getGlobalRecord();
		if(record == null)
			throw new RecordEx("not exist Record!");
		
		if(!(record instanceof RecordDB))
			throw new RecordEx("only RecordDB supported!");
		
		this.content_filter = Configure.getContentFilters();
		this.site_filter = Configure.getSiteFilters(); 
	
		this.root_site = root_site;
		
		this.record_data = (RecordDB)record.getInstance();		
		this.record_data.connect();
	}
	
	public int getSpiderId()
	{
		return this.id;
	}
	
	public Record getRecord()
	{
		return this.record_data;
	}

	private void registerSpider() throws RecordEx, ContentEx, UnknownHostException
	{		
		String info;
		String host;
		
		host = java.net.InetAddress.getLocalHost().getHostName();
		info = 	"user => " + System.getProperty("user.name") + ", "
			 + "jre => " + System.getProperty("java.vendor") + ";" + System.getProperty("java.version") + ", "
			 + "cpu => " + Runtime.getRuntime().availableProcessors() + " unit(s), "
			 + "os => " + System.getProperty("os.name") + "(" 
			 + System.getProperty("os.version") + ") " +System.getProperty("os.arch");

		this.id = (Integer)this.record_data.execProc("PROC_CreateSpider", Thread.currentThread().getId(), new Date(), null, "running", host, info);
		if(this.id <= -1)
			throw new RecordEx("can not create spider :(");

		ThreadResources.registerResource(ThreadResources.RSRC.SPIDER, this);
	}
	
	public void setDomainToks(int idx)
	{
		this.domain_toks = idx;
	}
	
	public void setMode(MODE mode)
	{
		this.mode = mode;
	}
	
	private Content fetchContent(Site current_site) throws ChannelEx, RecordEx, ContentEx
	{
		Channel channel;
		Content content;

		content = null;
		
		/* check if site pass filter */
		if(FilterSiteChain.passFilters(this.site_filter, current_site, true)) {					
			channel = ChannelManager.getChannel(current_site);
			if(channel != null) {
				/* get content */
				try { 
					content = channel.getContent(current_site); 
				} catch(ChannelEx ex) {
					content = null;
					ex.printStackTrace();
				}
				
				/* check if content not exist on database and pass filter then save content */
				if(content != null && !content.isCached() && FilterContentChain.passFilters(this.content_filter, content, true)) 						
					channel.saveContent();			
			}
		}	
		
		return content;
	}

	private Site getNextSite(Site site) throws ContentEx, RecordEx
	{
		Site ret_site;
	
		/* get reference per domain? */
		if(this.mode == MODE.FIRST_ONE_DOMAIN && (site instanceof SiteURL)) {
			URL site_url;
			String host;
			int port;
			
			site_url = ((SiteURL)site).getURL();

			host = site_url.getHost();
			port = site_url.getPort();
			if(port < 0)
				port = site_url.getDefaultPort();
			
			if(this.domain_toks != -1)
				host = URLUtils.getDomainToken(host, this.domain_toks);
		
			ret_site = SiteURL.getTMP(host, port);
			if(ret_site != null)
				return ret_site;
		}
			
		/* get any reference */
		return Site.getTMP();
	}
	
	public void doHunt() throws SiteEx
	{		
		Site current_site;
		
		current_site = this.root_site;
		
		if(current_site == null)
			throw new SiteEx("root site is null!!");

		try {
			/* clean temporal tables */
			Site.cleanTMP();
			
			do {
				Content content;
			
				/* get content */
				content = fetchContent(current_site);
				if(content != null) {
					ArrayList<Site> ref_list;
				
					/* content references */
					ref_list = (ArrayList<Site>)content.getReferences();
					if(ref_list != null)
						for(int i = 0; i < ref_list.size(); i++) {
							Site ref_site;

							/* get reference */
							ref_site = ref_list.get(i);
							
							/* check if this reference pass filter */
							if(FilterSiteChain.passFilters(this.site_filter, ref_site, true)) {
							
								/* save temporality site */
								ref_site.saveTMP();
							}						
						}
				}
				
				/* get next site from temporal */
				current_site = getNextSite(current_site);
				
			} while(current_site != null);		
			
		} catch(Exception ex) {
			ex.printStackTrace();
		}		
	}

	private void doEnd()
	{
		try { 
			this.record_data.execProc("PROC_UpdateSpider", this.id, new Date(), "finished");
		} catch(Exception ex) {
			ex.printStackTrace();
		}
	}
	
	public void run()
	{		
		try {			
			/* register spider */
			registerSpider();			
						
			/* hunt contents and sites!! */
			doHunt();			
		} catch(Exception ex) {
			ex.printStackTrace();
		} finally {
			doEnd();
		}

	}
}
